package com.fare.taluo.web.action;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.cyberneko.html.parsers.DOMParser;
import org.json.simple.JSONArray;
import org.json.simple.JSONValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import com.fare.taluo.BaseFdcAction;
import com.fare.taluo.application.crawl.AdaptorApplication;
import com.fare.taluo.application.crawl.TaskAdapterApplication;
import com.fare.taluo.application.plane.channel.SourceApplication;
import com.fare.taluo.vo.core.RequestEntityVO;
import com.fare.taluo.vo.core.SourceEntityVO;
import com.gargoylesoftware.htmlunit.HttpMethod;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.util.NameValuePair;

@Controller("adaptorSettingAction")
@Scope("prototype")
public class AdaptorSettingAction extends BaseFdcAction {

	/**
	 * 
	 */
	private static final long serialVersionUID = -5412501874460553669L;

	private static final Logger logger = LoggerFactory
			.getLogger(AdaptorSettingAction.class);

	private String locations;
	private String params;
	private String properties;
	private String source;
	private String methods;
	private Map<String, String> channels;

	private String targets;
	private InputStream inputStream;

	@Resource(name = "adaptorApplication")
	AdaptorApplication adaptorApplication;
	
	@Resource(name = "taskAdapterApplication")
	TaskAdapterApplication taskAdapterApplication;

	@Resource(name = "sourceApplication")
	SourceApplication sourceApplication;

	public InputStream getInputStream() {
		return inputStream;
	}

	public String toAddAdaptorSetting() throws Exception {
		List<SourceEntityVO> list = sourceApplication.findAllSourceEntity();
		channels = new HashMap<String, String>();
		for (SourceEntityVO e : list) {
			channels.put(String.valueOf(e.getId()), e.getName());
		}
		returnPageURL = "/jsp/adaptor/index.jsp";
		return "dispatcher";
	}

	public String toAddAdaptorSettingInfo() throws Exception {
		List<SourceEntityVO> list = sourceApplication.findAllSourceEntity();
		channels = new HashMap<String, String>();
		for (SourceEntityVO e : list) {
			channels.put(String.valueOf(e.getId()), e.getName());
		}
		returnPageURL = "/jsp/adaptor/index.jsp";
		return "dispatcher";
	}
	
	public String get() throws Exception {
		String from = "CAN";
		String to = "PEK";
		Calendar c = Calendar.getInstance();
		c.add(Calendar.DATE, 1);
		SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getInstance();
		sdf.applyPattern("yyyyMMdd");
		String s = sdf.format(c.getTime());
		InputStream ainputStream = getInputStream(from, to, s, getList());

		inputStream = ainputStream;

		logger.debug("get inputstream:" + inputStream);

		return SUCCESS;
	}

	public InputStream formatHTML(InputStream is) throws Exception {
		DOMParser parser = new DOMParser();
		parser.setProperty(
				"http://cyberneko.org/html/properties/default-encoding",
				"utf-8");
		InputStreamReader s = new InputStreamReader(is);
		BufferedReader in = new BufferedReader(s);
		parser.parse(new InputSource(in));
		Document doc = parser.getDocument();

		ByteArrayOutputStream os = new ByteArrayOutputStream();
		TransformerFactory trans = TransformerFactory.newInstance();
		trans.newTransformer().transform(new DOMSource(doc),
				new StreamResult(os));
		System.out
				.println("===============================Neko=========================================================");
		System.out.println(os.toString());

		ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray());
		return bais;
	}

	public String saveMappings() throws Exception {
		List<Map<String, Object>> locs = getList();

		logger.debug(locs.toString());
		for (Map<String, Object> map : locs) {

			RequestEntityVO entity = new RequestEntityVO();
			entity.setLocation(map.get("location").toString());
			entity.setMethod(map.get("method").toString());
			entity.setType(map.get("type").toString());
			entity.setSourceId(source);

			Map<String, String> ma = (Map<String, String>) map.get("params");

			Set<Map.Entry<String, String>> set = ma.entrySet();
			Map<String, String> m = new HashMap<String, String>();
			for (Map.Entry<String, String> e : set) {
				String key = e.getKey();
				String value = e.getValue();
				m.put(key, value);
			}
			entity.setParams(m);
			if (locs.get(locs.size() - 1) == map) {
				entity.setProperties(properties);
			}
			adaptorApplication.saveAdaptorEntity(entity);
		}

		inputStream = new ByteArrayInputStream(locs.toString()
				.getBytes("UTF-8"));
		return SUCCESS;
	}

	private List<Map<String, Object>> getList() throws Exception {
		JSONArray obj = (JSONArray) JSONValue.parseWithException(targets);
		List<Map<String, Object>> locas = obj;
		return locas;
	}

	private InputStream getInputStream(String from, String to, String date,
			List<Map<String, Object>> locations) throws Exception {
		WebClient webClient = new WebClient();
		WebRequest request = null;
		WebResponse response = null;

		for (Map<String, Object> map : locations) {
			String location = map.get("location").toString();

			location = convert(location, from, to, date);
			logger.debug(location);

			String method = map.get("method").toString();
			URL url = new URL(location);
			HttpMethod httpMethod = null;
			if (method.equalsIgnoreCase("get"))
				httpMethod = HttpMethod.GET;
			if (method.equalsIgnoreCase("post"))
				httpMethod = HttpMethod.POST;
			request = new WebRequest(url, httpMethod);

			Map<String, String> d = (Map<String, String>) map.get("params");
			Set<Map.Entry<String, String>> set = d.entrySet();
			List<NameValuePair> list = new ArrayList<NameValuePair>();
			for (Map.Entry<String, String> e : set) {
				String key = e.getKey();
				String value = e.getValue();
				value = convert(value, from, to, date);
				NameValuePair nameValuePair = new NameValuePair(key, value);
				list.add(nameValuePair);
			}
			request.setRequestParameters(list);
			response = webClient.loadWebResponse(request);
			logger.debug(response.getContentAsString());
		}

		// return formatHTML(response.getContentAsStream());
		return response.getContentAsStream();
	}

	private static String convert(String location, String from, String to,
			String date) throws ParseException {
		Pattern p = Pattern.compile("\\{\\s*from\\s*\\}",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(location);
		location = m.replaceAll(from);
		p = Pattern.compile("\\{\\s*to\\s*\\}", Pattern.CASE_INSENSITIVE);
		m = p.matcher(location);
		location = m.replaceAll(to);

		SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getInstance();
		sdf.applyPattern("yyyyMMdd");
		Date d = sdf.parse(date);
		p = Pattern.compile("\\{\\s*date\\s*,\\s*([^\\}]*)\\s*\\}",
				Pattern.CASE_INSENSITIVE);
		m = p.matcher(location);
		if (m.find()) {
			String g0 = m.group(1);
			sdf.applyPattern(g0);
			String d1 = sdf.format(d);
			location = m.replaceAll(d1);
		}

		return location;
	}

	public String getLocations() {
		return locations;
	}

	public void setLocations(String locations) {
		this.locations = locations;
	}

	public String getParams() {
		return params;
	}

	public void setParams(String params) {
		this.params = params;
	}

	public String getProperties() {
		return properties;
	}

	public void setProperties(String properties) {
		this.properties = properties;
	}

	public String getMethods() {
		return methods;
	}

	public void setMethods(String methods) {
		this.methods = methods;
	}

	public String getTargets() {
		return targets;
	}

	public void setTargets(String targets) {
		this.targets = targets;
	}

	public Map<String, String> getChannels() {
		return channels;
	}

	public void setChannels(Map<String, String> channels) {
		this.channels = channels;
	}

	public String getSource() {
		return source;
	}

	public void setSource(String source) {
		this.source = source;
	}

}
